跳到主要内容

JUnit 编写单元测试

说来惭愧,学习了那么久的编程连单元测试具体应该怎么用都不知道...

什么是 Junit

参考资料 JUnit5 官网 参考资料 廖雪峰老师的官网 老实说版本有点旧了,Junit5 与之有点不同

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>

在写这篇文章之前,一直都是拿 JUnit 当另一种 Main 方法来使用,实际上单元测试应该使用断言之类的工具

单元测试就是针对最小的功能单元编写测试代码。

Java程序最小的功能单元是方法,因此,对Java程序进行单元测试就是针对单个Java方法的测试。

如下:实际可以使用 assertEquals 来对期望结果进行比较

public class FactorialTest {

@Test
void testFact() {
assertEquals(1, Factorial.fact(1));
assertEquals(2, Factorial.fact(2));
assertEquals(6, Factorial.fact(3));
assertEquals(3628800, Factorial.fact(10));
assertEquals(2432902008176640000L, Factorial.fact(20));
}
}

测试分类 黑盒测试、白盒测试

黑盒测试

黑盒测试又称为功能测试,主要检测软件的每一个功能是否能够正常使用。

在测试过程中,将程序看成不能打开的黑盒子,不考虑程序内部结构 通过程序接口进行测试,检查程序功能是否按照设计需求能输出期望的结果(不需要写代码,给定输入值,看程序是否能返回期望值)

白盒测试

白盒测试也和黑盒测试一样需要输入输出,但不同的是,白盒测试主要目的是查看代码是怎么执行的,查看算法是否合理,效率是否高

测试规范

1、测试类名:被测试类的名字 + Test

2、包名:xxx.xxx.test

3、测试方法名:test + 被测方法名

4、测试方法的返回值为 void

5、测试方法空参

常用的断言方法

assertTrue(): 期待结果为 true assertFalse(): 期待结果为 false assertNotNull(): 期待结果为非 null assertArrayEquals(): 期待结果为数组并与期望数组每个元素的值均相等

class AssertionsDemo {

@Test
void standardAssertions() {
assertEquals(2, 2);

assertEquals(1, 2,"失败了会执行这条消息");

assertTrue(2 != 2, "失败了会执行这条消息");
}

@Test
void groupedAssertions() {
// 在一个分组的断言中,所有断言都被执行,任何失败都将一起报告。
assertAll("person",
() -> assertEquals("John", person.getFirstName()),
() -> assertEquals("Doe", person.getLastName())
);
}

@Test
void dependentAssertions() {
// 在代码块中,如果断言失败,将跳过同一块中的后续代码。
assertAll("properties",
() -> {
String firstName = person.getFirstName();
assertNotNull(firstName);

// Executed only if the previous assertion is valid.
assertAll("first name",
() -> assertTrue(firstName.startsWith("J")),
() -> assertTrue(firstName.endsWith("n"))
);
},
() -> {
// Grouped assertion, so processed independently
// of results of first name assertions.
String lastName = person.getLastName();
assertNotNull(lastName);

// Executed only if the previous assertion is valid.
assertAll("last name",
() -> assertTrue(lastName.startsWith("D")),
() -> assertTrue(lastName.endsWith("e"))
);
}
);
}

@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}

@Test
void timeoutNotExceeded() {
// 下面的断言执行成功
assertTimeout(ofMinutes(2), () -> {
// 这里面是执行时长小于 2 分钟的命令
});
}

@Test
void timeoutNotExceededWithResult() {
// 下面的断言成功,并返回提供的对象。
String actualResult = assertTimeout(ofMinutes(2), () -> {
return "a result";
});

assertEquals("a result", actualResult);
}

@Test
void timeoutNotExceededWithMethod() {
// 下面的断言调用一个方法引用并返回一个对象。
String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
assertEquals("hello world!", actualGreeting);
}

@Test
void timeoutExceeded() {
// The following assertion fails with an error message similar to:
// execution exceeded timeout of 10 ms by 91 ms
assertTimeout(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
Thread.sleep(100);
});
}

@Test
void timeoutExceededWithPreemptiveTermination() {
// The following assertion fails with an error message similar to:
// execution timed out after 10 ms
assertTimeoutPreemptively(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
Thread.sleep(100);
});
}

private static String greeting() {
return "hello world!";
}

}

@Before 和 @After

注意:@BeforeEach@BeforeAll 区别在于前者会在这个类下的所有测试方法执行前都执行一次(同时测试多个方法时),后者只执行一次且必须为静态方法

@AfterAll@AfterEach 的区别同上

class TempTestClassTest {

@BeforeAll
public static void init() {
System.out.println("在当前类的所有测试方法之前执行(只执行一次),一般用于初始化工作,且必须是静态的");
}


@BeforeEach
public void init02() {
System.out.println("在每个测试方法之前执行(每个测试方法前执行),一般用于初始化工作");
}


@AfterAll
public static void close(){
System.out.println("在当前类的所有测试方法之后执行(只执行一次),必须是静态的");
}

@AfterEach
public void close02(){
System.out.println("在每个测试方法之后执行(每个测试方法前执行)");
}

@Test
public void test() {
TempTestClass aClass = new TempTestClass();
System.out.println("执行 +++++++++++++++++++++++++++");
assertEquals(1, aClass.firstUnitTesting());
}

@Test
public void test02() {
TempTestClass aClass = new TempTestClass();
System.out.println("执行 00000000000000000000000000");
assertEquals(1, aClass.firstUnitTesting());
}
}

输出结果

在当前类的所有测试方法之前执行(只执行一次),一般用于初始化工作,且必须是静态的

在每个测试方法之前执行(每个测试方法前执行),一般用于初始化工作
执行 00000000000000000000000000
在每个测试方法之后执行(每个测试方法前执行)


在每个测试方法之前执行(每个测试方法前执行),一般用于初始化工作
执行 +++++++++++++++++++++++++++
在每个测试方法之后执行(每个测试方法前执行)

在当前类的所有测试方法之后执行(只执行一次),必须是静态的

批量运行测试

网上的垃圾教程真尼玛多讲个东西讲半天还尼玛讲不清而且大部分都是使用老版本的东西,实际上批量执行测试类只需使用类旁边那个测试按钮就行了...

image.png

集成测试

TODO: 用到集成测试再补充吧